package com.tzy.mydb.backend.dm;

import com.tzy.mydb.backend.dm.dataItem.DataItem;
import com.tzy.mydb.backend.dm.logger.Logger;
import com.tzy.mydb.backend.dm.page.PageOne;
import com.tzy.mydb.backend.dm.pageCache.PageCache;
import com.tzy.mydb.backend.tm.TransactionManager;

/**
 * DataManager 是 DM 层直接对外提供方法的类，
 * 同时，也实现成 DataItem 对象的缓存。
 * DataItem 存储的 key，是由页号和页内偏移组成的一个 8 字节无符号整数，页号和偏移各占 4 字节。
 */
public interface DataManager {
    /**
     * 从DataItem缓存中读取一个DataItem数据包并进行校验，
     * 如果DataItem缓存中没有就会调用 DataManager下的getForCache(long uid)从PageCache缓存中读取DataItem数据包并加入DataItem缓存
     * 如果PgeCache也没有就去数据库文件读取。
     * @param uid
     * @return
     * @throws Exception
     */
    DataItem read(long uid) throws Exception;

    /**
     *先把数据打包成DataItem格式，
     * 然后在 pageIndex 中获取一个足以存储插入内容的页面的页号；
     * 获取页面后，需要先写入插入日志，
     * 接着才可以通过在目标数据页插入数据，并返回插入位置的偏移。
     * 如果在pageIndex中没有空闲空间足够插入数据了，就需要新建一个数据页。
     * 最后需要将页面信息重新插入 pageIndex。
     * @param xid
     * @param data
     * @return
     * @throws Exception
     */
    long insert(long xid, byte[] data) throws Exception;

    void close();

    /**
     * 从已有文件创建 DataManager 和从空文件创建 DataManager 的流程稍有不同，
     * PageCache 和 Logger 的创建方式有所不同
     * 从空文件创建首先需要对第一页进行初始化
     * @param path
     * @param mem
     * @param tm
     * @return
     */
    public static DataManager create(String path, long mem, TransactionManager tm) {
        PageCache pc = PageCache.create(path, mem);
        Logger lg = Logger.create(path);

        DataManagerImpl dm = new DataManagerImpl(pc, lg, tm);
        dm.initPageOne();
        return dm;
    }

    /**
     * * 从已有文件创建 DataManager 和从空文件创建 DataManager 的流程稍有不同，
     * * PageCache 和 Logger 的创建方式有所不同
     * 从已有文件创建，则是需要对第一页进行校验，来判断是否需要执行恢复流程。并重新对第一页生成随机字节
     * @param path
     * @param mem
     * @param tm
     * @return
     */
    public static DataManager open(String path, long mem, TransactionManager tm) {
        PageCache pc = PageCache.open(path, mem);
        Logger lg = Logger.open(path);
        DataManagerImpl dm = new DataManagerImpl(pc, lg, tm);
        //校验失败，执行恢复流程
        if(!dm.loadCheckPageOne()) {
            Recover.recover(tm, lg, pc);
        }
        dm.fillPageIndex();
        PageOne.setVcOpen(dm.pageOne);
        dm.pc.flushPage(dm.pageOne);

        return dm;
    }
}
